[WIP] Run FixLegacyResourceDesigner before trimming#11084
[WIP] Run FixLegacyResourceDesigner before trimming#11084
Conversation
Migrate FixLegacyResourceDesignerStep out of the ILLink trimmer process and into PostTrimmingPipeline, continuing the work in #10842 to remove custom ILLink steps. The step now runs after ILLink via a thin wrapper (PostTrimmingFixLegacyResourceDesignerStep) that calls ProcessAssemblyDesigner() directly, matching the former ILLink behavior. - Remove all #if ILLINK conditionals from FixLegacyResourceDesignerStep, LinkDesignerBase, and BaseStep - Remove FixLegacyResourceDesignerStep and LinkDesignerBase from the ILLink project (no longer compiled as a trimmer step) - Add UseDesignerAssembly property to PostTrimmingPipeline task - Wire AndroidUseDesignerAssembly through targets to PostTrimmingPipeline - Remove _TrimmerCustomSteps entry for FixLegacyResourceDesignerStep
In NativeAOT builds, _AddResourceDesignerToPublishFiles ran after _ComputeManagedAssemblyToLink, so the designer assembly was not in ManagedAssemblyToLink. ILLink skipped the designer assembly entirely and did not rewrite its netstandard references. Fix by adding _AddResourceDesignerToPublishFiles to _PrepareLinking's DependsOnTargets, so the designer is passed to ILLink.
# Conflicts: # src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj
FixAbstractMethodsStep was moved from ILLink to the post-trim pipeline on main (7ae24aa). The merge conflict resolution incorrectly kept the Compile include, but the file now uses types not available in the ILLink project (IAssemblyModifierPipelineStep, StepContext, Properties.Resources).
…rties FixLegacyResourceDesignerStep now runs in PostTrimmingPipeline (after ILLink) instead of as an ILLink custom step. Previously, the step ran before MarkStep and rewrote library assemblies to reference designer properties, which caused MarkStep to preserve them. Now that the rewriting happens after ILLink, we must root the designer assembly so all resource properties survive trimming. Add TrimmerRootAssembly for the designer in _AddResourceDesignerToPublishFiles. This keeps IsTrimmable=true (action=link) so ILLink still rewrites the netstandard assembly reference, but roots all types so nothing is trimmed. An alternative approach would be to run FixLegacyResourceDesignerStep before ILLink instead of after. That would allow ILLink to trim unused resource properties from the designer (since the rewritten references would already be in place for MarkStep), but it would also process resource references in assemblies that ILLink may later remove entirely.
… assembly TrimmerRootAssembly (-a flag) makes the designer an entry point, which causes ILLink to retain netstandard.dll as a dependency, leaking it into the APK (+19KB). TrimmerRootDescriptor (-x flag) preserves all designer types/members without making it an entry point, avoiding the regression.
…builds FixLegacyResourceDesignerStep now runs in PostTrimmingPipeline (MSBuild task) instead of inside ILLink, so the error code is always XA8000 regardless of build configuration.
WriteLinesToFile was regenerating the TrimmerRootDescriptor XML on every build, changing its timestamp and causing ILLink to re-run on incremental builds. Adding WriteOnlyWhenDifferent preserves the timestamp when the content hasn't changed.
Run FixLegacyResourceDesignerStep *before* ILLink instead of after it, so the trimmer sees the rewritten IL (call instructions to designer property getters) and can freely trim unused designer types. This removes the need to root the entire designer assembly via a TrimmerRootDescriptor during trimming, eliminating the APK size regression.
There was a problem hiding this comment.
Pull request overview
This PR explores running the legacy resource designer rewrite before ILLink so that ILLink can trim unused members from the shared resource designer assembly (avoiding the APK size regression introduced when moving the step post-trim in #11059).
Changes:
- Add a new MSBuild task (
PreTrimmingFixLegacyDesigner) and wire it to run beforeILLinkwhen trimming withAndroidUseDesignerAssembly=True. - Remove the legacy designer rewrite from ILLink custom steps and adjust linker/shared code to no longer compile under the ILLink project.
- Update a device integration test expectation to always report
XA8000(instead ofIL8000in some cases).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs | Updates expected error code to XA8000 now that the failure is reported outside ILLink. |
| src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj | Ensures needed linker sources (incl. LinkDesignerBase) are compiled into build tasks. |
| src/Xamarin.Android.Build.Tasks/Tasks/PreTrimmingFixLegacyDesigner.cs | Introduces the pre-trim task that rewrites assemblies in-place prior to ILLink. |
| src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets | Documents why a root descriptor is no longer needed with the pre-trim rewrite approach. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | Wires the new pre-trim task before ILLink and removes the old ILLink custom step hook. |
| src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs | Removes ILLink-conditional logging paths now that this code no longer runs inside ILLink. |
| src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs | Makes the step consistently implement the build-task pipeline interface and removes ILLink-only code paths. |
| src/Xamarin.Android.Build.Tasks/Linker/External/Linker.Steps/BaseStep.cs | Removes ILLink-conditional logging path to match non-ILLink usage. |
| src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj | Stops compiling the legacy designer step into the ILLink tool assembly. |
src/Xamarin.Android.Build.Tasks/Tasks/PreTrimmingFixLegacyDesigner.cs
Outdated
Show resolved
Hide resolved
...droid.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets
Show resolved
Hide resolved
src/Xamarin.Android.Build.Tasks/Tasks/PreTrimmingFixLegacyDesigner.cs
Outdated
Show resolved
Hide resolved
FixLegacyResourceDesignerStep now runs in PostTrimmingPipeline for all runtimes including NativeAOT, so the missing @styleable/SKCanvasView resources correctly produce XA8000 errors. Remove the NativeAOT exclusion from the addResource=False path.
The CI failures are caused by the same satellite assembly file-locking issue tracked in #11085: |
Both _PreTrimmingFixLegacyDesigner and _PostTrimmingPipeline were including all .dll files from ResolvedFileToPublish, which includes satellite resource assemblies (.resources.dll) from the shared NuGet package cache. These tasks open assemblies with ReadWrite access via DirectoryAssemblyResolver, causing IOException when parallel multi-RID builds contend for locks on the same cached files. Filter both targets to only process items where PostprocessAssembly metadata is 'true', which excludes satellite assemblies that ILLink does not process. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The resolver was using ReadWrite=true, which acquires exclusive file locks on every assembly it loads — including the shared _Microsoft.Android.Resource.Designer.dll that is loaded implicitly via Resolve() during FixLegacyResourceDesignerStep.LoadDesigner(). The designer DLL is built once in the outer build at a shared obj/ path, but this task runs in each inner per-RID build. Parallel RID builds contend for exclusive locks on the shared file, causing IOException. Since the task only reads the designer assembly (never writes it), drop ReadWrite=true so all assemblies are opened in shared read mode. Library assemblies that get modified are written back using an explicit output path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#11059 is moving this step to run after trimming.
This comes with an APK size regression because ILLink no longer has a chance to trim the shared designer assembly.
This change is exploring making it a pre-trim step instead.